package com.woniu.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.account.client.ProductServerClient;
import com.woniu.account.client.dto.TUserCourseDto;
import com.woniu.account.dao.TCoachInfoMapper;
import com.woniu.account.dao.TCoachMapper;
import com.woniu.account.dao.TUserInfoMapper;
import com.woniu.account.dto.*;
import com.woniu.account.form.*;
import com.woniu.account.model.*;
import com.woniu.account.model.TCoach;
import com.woniu.account.service.TCoachService;
import com.woniu.account.service.TLoginLogService;
import com.woniu.core.exception.account.AccountException;
import com.woniu.core.exception.account.AccountExceptionCode;
import com.woniu.core.exception.gym.GymException;
import com.woniu.core.exception.gym.GymExceptionCode;
import com.woniu.core.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

import static com.woniu.core.exception.account.AccountExceptionCode.LOGIN_ERROR;

import javax.annotation.Resource;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 吴彦祖
 * @since 2024年04月02日
 */
@Service
public class TCoachServiceImpl extends ServiceImpl<TCoachMapper, TCoach> implements TCoachService {
    @Resource
    private TCoachMapper tCoachMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private TCoachInfoMapper tCoachInfoMapper;
    @Autowired
    private TLoginLogService tLoginLogService;
    @Resource
    private AliyunOSSUtil aliyunOSSUtil;
    @Resource
    private TUserInfoMapper tUserInfoMapper;
    @Resource
    private ProductServerClient productServerClient;
    @Override
    public CoachDto getCoachById(Integer coachId) {
        TCoach coach = tCoachMapper.selectOne(new QueryWrapper<TCoach>().eq("coach_id", coachId)
                .eq("coach_state", 1));
        if (coach == null) throw new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        CoachDto dto = BeanUtil.toBean(coach, CoachDto.class);
        return dto;
    }

    @Override
    public TCoach coachAccountPerfect(updateCoachInformationForm form) throws Exception {
        Integer adminId = JwtUtil.getUserId(3);
        TCoach tCoach = tCoachMapper.selectById(adminId);
        if(tCoach==null)throw new AccountException("用户不存在",40001);
        tCoach.setCoachEmail(form.getCoachEmail());
        tCoach.setCoachPhone(form.getCoachPhone());
        tCoach.setCoachImage(form.getCoachImage());
        tCoach.setCoachNickname(form.getCoachNickname());
        tCoach.setRemark(form.getRemark());
        tCoachMapper.updateById(tCoach);
        return tCoach;
    }

    @Override
    public TCoach teacherRegister(RegisterCoachForm form) throws Exception {
        List<TCoach> list = list(new QueryWrapper<TCoach>()
                .eq("coach_name", form.getCoachName()).or()
                .eq("coach_phone", form.getCoachPhone()));
        if (list.size()>0) throw new AccountException(AccountExceptionCode.THIS_ACCOUNT_HAS_BEEN_REGISTERED);
        TCoach tCoach = BeanUtil.toBean(form, TCoach.class);
        tCoach.setCoachState(1);
        String salt = form.getCoachName();
        int index = 0;
        int count = 2;
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8),index,count);
        tCoach.setCoachPass(md5.digestHex(form.getCoachPass()));
        tCoach.setCoachState(2);
        save(tCoach);
        //新增coachInfo表的相同字段:coachid\coachphone\remark
        //剩下的部分在实名认证时区补全
        TCoachInfo tCoachInfo = new TCoachInfo();
        tCoachInfo.setCoachId(tCoach.getCoachId());
        tCoachInfo.setCoachPhone(tCoach.getCoachPhone());
        tCoachInfo.setRemark(tCoach.getRemark());
        tCoachInfoMapper.insert(tCoachInfo);
        return tCoach;
    }

    @Override
    public CoachLoginDto teacherLogin(LoginCoachForm form) throws Exception {
        //判断账号密码是否正确
        TCoach admin = getOne(new QueryWrapper<TCoach>()
                .eq("coach_name", form.getCoachName()));
        if (admin==null) throw new AccountException("账号不存在",400001);

        String salt = form.getCoachName();
        int index = 0;
        int count = 2;
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8),index,count);
        if(!md5.digestHex(form.getCoachPass()).equals(admin.getCoachPass())){
            throw new AccountException("密码错误",400001);
        }
        if(admin.getCoachState()!= 1){
            throw new AccountException("账号状态异常",400001);
        }
        //获得返回对象
        CoachLoginDto dto = BeanUtil.toBean(admin, CoachLoginDto.class);
        //创建token
        Map<String,Object> maps = new HashMap<>();
        maps.put("coachId", dto.getCoachId());
        maps.put("coachRole", dto.getCoachName());
        //创建短token
        String accessToken = JwtUtil.createToken(maps, 30,3);
        //创建长token,续期
        String refreshToken = JwtUtil.createToken(maps, 60 * 24,3);
        //token为键，长token为值，存入redis
        redisUtil.set(accessToken,refreshToken,24L*60L*60L);
        //将token放入响应头
        WebUtil.getResponse().setHeader("access",accessToken);
        WebUtil.getResponse().addHeader("Access-Control-Expose-Headers","access");

        //添加登录日志
        TLoginLog log = new TLoginLog();
        log.setLoginRole(2);
        log.setAccountId(admin.getCoachId());
        log.setAccountName(admin.getCoachName());
        String ipAddr = JwtUtil.getIpAddr(WebUtil.getRequest());
        log.setAddressIp(ipAddr);
        log.setLogInfo("登录成功");
        log.setLogDate(LocalDateTime.now());
        boolean b = tLoginLogService.save(log);
        if (!b) throw new AccountException(LOGIN_ERROR);
        return dto;    }

    @Override
    public void sendLoginCode(String phone) throws Exception {
        //发送登录验证码时，要验证手机号是否注册过，注册过才发验证码
        QueryWrapper<TCoach> wrapper = new QueryWrapper<TCoach>();
        wrapper.eq("coach_phone", phone);
        TCoach userInfo = tCoachMapper.selectOne(wrapper);
        if (userInfo == null) {
            throw new AccountException("手机号还未注册",400001);
        }
        String code = "123456";
        //redis保存一份验证码，为了登录时的验证
        redisUtil.set(RedisKeyUtil.sendLoginCodeKey(phone), code, 300);
        //一分钟内不能同时发送的标识
        redisUtil.set(RedisKeyUtil.sendLoginCodeInMinute(phone), "", 60);
        //判断24小时内的次数限定
        if (redisUtil.exist(RedisKeyUtil.sendCodeNumberOn24hour(phone))) {
            //存在就调用util自增1的方法，为了判断是否达到5条
            redisUtil.incr(RedisKeyUtil.sendCodeNumberOn24hour(phone));
        } else {
            //如果没有发送过的话，就要加1
            redisUtil.set(RedisKeyUtil.sendCodeNumberOn24hour(phone), "1", 24 * 60 * 60);
        }
//        return true;
    }

    @Override
    public CoachLoginDto coachCodeLogin(CoachCodeLoginForm form) throws Exception {
        //取出之前的登录验证码key
        //前端传过来的是用Interger的类型，这里就要强转
        int code = Integer.parseInt(redisUtil.get(RedisKeyUtil.sendLoginCodeKey(form.getCoachPhone())));
        if (form.getCode() != code) {
            if (!redisUtil.exist(RedisKeyUtil.errorTotal(form.getCoachPhone()))) {
                redisUtil.set(RedisKeyUtil.errorTotal(form.getCoachPhone()), "1", 5 * 60);
            } else {
                redisUtil.incr(RedisKeyUtil.errorTotal(form.getCoachPhone()));
            }
            throw new AccountException("验证码错误",400001);
        }
        //登录就不用判断了，因为收到了验证码，证明无异常,直接查
        QueryWrapper<TCoach> wrapper = new QueryWrapper<>();
        wrapper.eq("coach_phone", form.getCoachPhone());
        TCoach tCoach = tCoachMapper.selectOne(wrapper);
        CoachLoginDto dto = BeanUtil.toBean(tCoach, CoachLoginDto.class);
        //创建token
        Map<String,Object> maps = new HashMap<>();
        maps.put("coachId", dto.getCoachId());
        maps.put("coachRole", dto.getCoachName());
        //创建短token
        String accessToken = JwtUtil.createToken(maps, 30,3);
        //创建长token,续期
        String refreshToken = JwtUtil.createToken(maps, 60 * 24,3);
        //token为键，长token为值，存入redis
        redisUtil.set(accessToken,refreshToken,24L*60L*60L);
        //将token放入响应头
        WebUtil.getResponse().setHeader("access",accessToken);
        WebUtil.getResponse().addHeader("Access-Control-Expose-Headers","access");

        //添加登录日志
        TLoginLog log = new TLoginLog();
        log.setLoginRole(2);
        log.setAccountId(tCoach.getCoachId());
        log.setAccountName(tCoach.getCoachName());
        String ipAddr = JwtUtil.getIpAddr(WebUtil.getRequest());
        log.setAddressIp(ipAddr);
        log.setLogInfo("登录成功");
        log.setLogDate(LocalDateTime.now());
        boolean b = tLoginLogService.save(log);
        if (!b) throw new AccountException(LOGIN_ERROR);
        return dto;
    }

    @Override
    public void coachChangePassword(ChangePasswordForm form) throws Exception {
        Integer adminId = JwtUtil.getUserId(3);
        TCoach tAccount = tCoachMapper.selectById(adminId);
        if(tAccount==null){
            throw  new Exception("信息不存在请确认");
        }
        String salt = tAccount.getCoachName();
        int index=0;//盐的位移数，底层逻辑是通过位移进行加密的
        int count=2;//加密的次数
        //MD5的加密API
        MD5 md5 = new MD5(salt.getBytes(StandardCharsets.UTF_8), index, count);
        if(!tAccount.getCoachPass().equals(md5.digestHex(form.getAccountPass()))){
            throw new AccountException("旧密码输入不正确",400001);
        }
        if(!form.getNewAccountPass().equals(form.getNewAccountPass1())){
            throw new AccountException("两次密码输入不同",400001);
        }
        UpdateWrapper<TCoach> wra = new UpdateWrapper<TCoach>();
        wra.set("coach_pass",md5.digestHex(form.getNewAccountPass()));
        wra.eq("coach_id",tAccount.getCoachId());
        tCoachMapper.update(null,wra);
//        boolean b = updateById(tAccount);
//        if (!b) throw new AccountException("修改失败",40000);
////        return true;
    }

    @Override
    public CoachDto coachBindGym(Integer coachId,String gymNUm) {
        TCoach tCoach = tCoachMapper.selectById(coachId);
        if (tCoach == null) throw new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        tCoach.setGymNum(gymNUm);
        tCoachMapper.updateById(tCoach);
        CoachDto dto = BeanUtil.toBean(tCoach, CoachDto.class);
        return dto;
    }

    @Override
    public String uploadImage(UpdateImageForm form) throws Exception {
        TCoach tCoach = tCoachMapper.selectById(form.getAccountId());
        String fileType = FileTypeUtil.getType(form.getImage().getInputStream());
        if(!fileType.equals("jpg")&&!fileType.equals("png")){
            throw new Exception("图片格式不正确");
        }

        String suffix = form.getImage().getOriginalFilename().substring(form.getImage().getOriginalFilename().lastIndexOf("."));
        String name = UUID.randomUUID().toString();
        //String path = aliyunOSSUtil.uploadFile(param.getImage().getInputStream(), name + suffix, "ryusai");
        //新的图片地址
        String url = aliyunOSSUtil.uploadFile(form.getImage().getInputStream(), name + suffix, "ryusai");
        //老的图片地址
        String oldUrl = tCoach.getCoachImage();
        try {
            tCoachMapper.updateById(tCoach);
        }catch (Exception e){
            //没有新增成功
            aliyunOSSUtil.deleteFile("ryusai", FileUtil.getName(url));
        }
        //新增成功了删掉
        aliyunOSSUtil.deleteFile("ryusai",FileUtil.getName(oldUrl));
        tCoach.setCoachImage(url);
        tCoachMapper.updateById(tCoach);
        return url;
    }

    @Override
    public CoachDto coachUntieGym(Integer coachId) {
        TCoach tCoach = tCoachMapper.selectById(coachId);
        if (tCoach == null) throw new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        if(tCoach.getGymNum()==null || tCoach.getGymNum().equals("")){
            throw new GymException(GymExceptionCode.GYM_NOT_EXIST);
        }
        tCoach.setGymNum("");
        tCoachMapper.updateById(tCoach);
        CoachDto dto = BeanUtil.toBean(tCoach, CoachDto.class);
        return dto;
    }

    @Override
    public PageInfo<MyFansDto> myFans(Integer pageNum, Integer pageSize,Integer coachId) throws Exception {
        pageNum = (pageNum == null) ? 1 : pageNum;
        pageSize= (pageSize == null) ? 5 : pageSize;
        //先查询收藏列表总数
        long total = redisUtil.zcard(RedisKeyUtil.userFollowListKey(coachId));
        long pages = total%pageSize==0?total/pageSize:total/pageSize+1;
        long start = (pageNum-1)*pageSize;
        long end = pageSize*pageNum-1;
        if(redisUtil.zcard(RedisKeyUtil.userFollowListKey(coachId))!=0){
            Set<String> set = redisUtil.zrevrange(RedisKeyUtil.userFollowListKey(coachId), start, end);

            ArrayList<MyFansDto> data = new ArrayList<>();

            for(String userId:set){
                TUserInfo tUserInfo = tUserInfoMapper.selectById(userId);
                MyFansDto dto = new MyFansDto();
                dto.setUserName(tUserInfo.getUserName());
                dto.setUserImage(tUserInfo.getUserImage());
                data.add(dto);
            }
            PageInfo<MyFansDto> pageInfo = new PageInfo<MyFansDto>();
            pageInfo.setTotal(total);
            pageInfo.setPages(pages);
            pageInfo.setCurrent(Long.valueOf(pageNum));
            pageInfo.setSize(Long.valueOf(pageSize));
            pageInfo.setList(data);
            return pageInfo;
        }else {
            //解决前端为null的时候控制台报错问题
            PageInfo<MyFansDto> pageInfo = new PageInfo<>();
            pageInfo.setCurrent(Long.valueOf(pageNum));
            pageInfo.setSize(Long.valueOf(pageSize));
            return pageInfo;

        }

    }

    @Override
    public PageInfo<FindUnsignedCoachDto> findUnsignCoach(FindUnsignCoachForm form) {
        form.setPageNum(form.getPageNum() == null?1:form.getPageNum());
        form.setPageSize(form.getPageSize()==null?5:form.getPageSize());

        QueryWrapper<TCoach> wrapper = new QueryWrapper<>();
        wrapper.isNull("gym_num").or().eq("gym_num","").eq("coach_state",1);
        List<TCoach> list = tCoachMapper.selectList(wrapper);
        ArrayList<FindUnsignedCoachDto> data = new ArrayList<>();
        for (TCoach tCoach : list) {
            TCoachInfo tCoachInfo = tCoachInfoMapper.selectOne(new QueryWrapper<TCoachInfo>().eq("coach_id",tCoach.getCoachId())
                    .eq("resume_state",1));
            if(tCoachInfo==null){
                continue;
            }
            FindUnsignedCoachDto dto = BeanUtil.toBean(tCoach, FindUnsignedCoachDto.class);
            dto.setCoachPhoto(tCoachInfo.getCoachPhoto());
            dto.setCoachIdnum(tCoachInfo.getCoachIdnum());
            dto.setCoachGender(tCoachInfo.getCoachGender());
            dto.setCoachAge(tCoachInfo.getCoachAge());
            dto.setCoachAuthen(tCoachInfo.getCoachAuthen());
            dto.setCoachDetails(tCoachInfo.getCoachDetails());
            dto.setCoachMoney(tCoachInfo.getCoachMoney());
            dto.setWantJob(tCoachInfo.getWantJob());
            dto.setCoachGood(tCoachInfo.getCoachGood());
            data.add(dto);
        }
        Integer total = data.size();
        Integer pages = total%form.getPageSize()==0?total/form.getPageSize():total/ form.getPageSize()+1;
        int fromIndex =  Math.min(Math.max(0, form.getPageNum() - 1) * form.getPageSize(), total);
        int toIndex = Math.min(fromIndex + form.getPageSize(), total);
        List<FindUnsignedCoachDto> pageData = data.subList(fromIndex, toIndex);

        PageInfo<FindUnsignedCoachDto> pageInfo = new PageInfo<>();
        pageInfo.setTotal(Long.valueOf(total));
        pageInfo.setPages(Long.valueOf(pages));
        pageInfo.setCurrent(Long.valueOf(form.getPageNum()));
        pageInfo.setSize(Long.valueOf(form.getPageSize()));
        pageInfo.setList(pageData);
        return pageInfo;
    }

    @Override
    public void sendRegisterCode(String phone) throws Exception {
        //判断当前手机号在1分钟内是否发送过
        if(redisUtil.exist(RedisKeyUtil.sendRegisterCodeInMinute(phone))){
            throw new Exception("一分钟内不能再次发送验证码");
        }
        //根据手机号查询用户信息
        QueryWrapper<TCoach> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("coach_phone",phone);
        TCoach tCoach = tCoachMapper.selectOne(queryWrapper);
        if(tCoach!=null){
            throw new AccountException("手机号已被注册",400001);
        }

        //调用SMS工具发送短信验证码
//        String code = smsUtil.send(phone);
        String code = "147258";
        redisUtil.set(RedisKeyUtil.sendRegisterCodeKey(phone),code,300L);
        //保存一个1分钟过期的key
        redisUtil.set(RedisKeyUtil.sendRegisterCodeInMinute(phone),"",60L);
    }

    @Override
    public List<CoachqueryMyStudentDto> queryMyStudentByCondition(Integer courseVariety) throws Exception {
        Integer coachId = JwtUtil.getUserId(3);
        if(coachId==null) throw new AccountException(AccountExceptionCode.USER_NOT_LOGIN);
        TCoach tCoach = tCoachMapper.selectById(coachId);
        if(tCoach==null) throw new AccountException(AccountExceptionCode.NOT_FIND_THIS_USER);
        if(tCoach.getCoachState()==0) throw new AccountException(AccountExceptionCode.COACH_ACCOUNT_IS_FORZEN_AND_CANT_BE_VIEWED);
        //查出t_user_course中我的学员id
        Result<List<TUserCourseDto>> result = productServerClient.queryMyStudent(coachId);
        //在通过id查出学员的相关信息，存进CoachqueryMyStudentDto中返回给前端
        if (result.getCode() != 200) {
            // 处理异常情况，可以抛出自定义异常或进行其他处理
            throw new AccountException("还没有学员，无法查询",400003);
        }
        List<TUserCourseDto> list = result.getData();
        List<CoachqueryMyStudentDto> data = new ArrayList<>();
        for (TUserCourseDto student : list) {
            CoachqueryMyStudentDto dto = BeanUtil.toBean(student, CoachqueryMyStudentDto.class);
            TUserInfo tUserInfo = tUserInfoMapper.selectById(student.getUserId());
            dto.setUserAge(tUserInfo.getUserAge());
            dto.setUserEmail(tUserInfo.getUserEmail());
            dto.setUserName(tUserInfo.getUserName());
            dto.setUserPhone(tUserInfo.getUserPhone());
            dto.setUserGender(tUserInfo.getUserGender());
            data.add(dto);
        }
        return data;
    }
}